<template>
  <div
    id="Panorama"
    class="Panorama"
    ref="panoramaContainer"
    :style="{
      width: props.widthSize ? `${props.widthSize}px` : '100%',
      height: props.heightSize ? `${props.heightSize}px` : `${defaultHeight}`,
    }"
  ></div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js'

// DOM容器
const panoramaContainer = ref<HTMLDivElement | null>(null)
let scene: THREE.Scene | null = null
let camera: THREE.PerspectiveCamera | null = null
let renderer: THREE.WebGLRenderer | null = null
let controls: OrbitControls | null = null

const emit = defineEmits(['canvas-ready'])

// 响应式变量
const cssRenderer = ref<CSS2DRenderer | null>(null)
const animationId = ref<number | null>(null)
const hotspotObj = ref<CSS2DObject | null>(null)
const CUBE_HALF_SIZE = 50 
const errorMessage = ref('')
const defaultHeight = document.documentElement.clientHeight
let axesHelper: THREE.AxesHelper | null = null

// 类型定义
interface Cooradinate {
  x?: number
  y?: number
  z?: number
  axisH?: { x: number; y: number; z: number }
  fov?: number
  heading?: number
  localY_angle_degrees?: number
  pitch?: number
  radius?: number
}

const props = defineProps({
  images: {
    type: Array<string>,
    required: true,
    validator: (val: string[]) => val.length === 6,
  },
  cooradinate: {
    type: Object as () => Cooradinate,
    required: false,
    default: () => ({}),
  },
  worldHotspot: {
    type: Object as () => { x: number; y: number; z: number } | null,
    default: null,
  },
  showHotspot: {
    type: Boolean,
    default: true,
  },
  widthSize: {
    type: Number,
    default: 0,
  },
  heightSize: {
    type: Number,
    default: document.documentElement.clientHeight,
  },
})

// 切换FOV（保留原有功能）
const switchFov = () => {
  if (!scene || !camera || !renderer || !controls) return
  // 切换时保持与滚轮调整的FOV范围一致
  const targetFov = camera.fov === 75 ? props.cooradinate?.fov || 50 : 75
  camera.fov = Math.max(20, Math.min(75, targetFov)) // 限制FOV范围
  camera.updateProjectionMatrix()
  controls.update()
  renderer.render(scene, camera)
}

// 滚轮调整FOV的事件处理函数
const handleWheelForFov = (event: WheelEvent) => {
  if (!camera || !controls) return
  // 根据滚轮方向调整FOV
  const fovStep = 2
  const newFov = event.deltaY < 0 ? camera.fov - fovStep : camera.fov + fovStep
  // 限制FOV在合理范围
  camera.fov = Math.max(20, Math.min(75, newFov))
  camera.updateProjectionMatrix()
  // 手动触发 controls 的 change 事件，使得父组件能捕获滚轮缩放操作
  // @ts-ignore - dispatchEvent 存在于 OrbitControls 上
  controls.dispatchEvent({ type: 'change' })
}

// 加载6个面的纹理
const loadCubeTextures = async (
  imageUrls: string[]
): Promise<THREE.MeshBasicMaterial[]> => {
  const textureLoader = new THREE.TextureLoader()
  textureLoader.crossOrigin = 'anonymous'
  imageUrls.forEach((url, index) => {
    if (!url || url.trim() === '') {
      console.warn(`图片URL无效，索引: ${index}`)
    }
  })

  const createBlackTexture = () => {
    const canvas = document.createElement('canvas')
    canvas.width = 1024
    canvas.height = 1024
    const ctx = canvas.getContext('2d')
    if (ctx) {
      ctx.fillStyle = '#000000'
      ctx.fillRect(0, 0, canvas.width, canvas.height)
    }
    const texture = new THREE.CanvasTexture(canvas)
    texture.wrapS = THREE.ClampToEdgeWrapping
    texture.wrapT = THREE.ClampToEdgeWrapping
    return texture
  }

  const texturePromises = imageUrls.map((url, index) => {
    if (!url || url.trim() === '') {
      return Promise.resolve(createBlackTexture())
    }

    return new Promise<THREE.Texture>((resolve, reject) => {
      textureLoader.load(
        url,
        (texture: THREE.Texture) => {
          texture.wrapS = THREE.ClampToEdgeWrapping
          texture.wrapT = THREE.ClampToEdgeWrapping
          texture.magFilter = THREE.LinearFilter
          texture.minFilter = THREE.LinearFilter
          texture.generateMipmaps = false

          if ([0, 4, 5].includes(index)) {
            (texture as any).flipX = true
          } else {
            (texture as any).flipX = false
          }
          texture.colorSpace = THREE.SRGBColorSpace
          resolve(texture)
        },
        undefined,
        (error) => {
          console.error(`图片加载失败，URL: ${url}`, error)
          errorMessage.value = `图片加载失败: ${url}`
          resolve(createBlackTexture())
        }
      )
    })
  })

  const textures = await Promise.all(texturePromises)
  return textures.map(
    (texture) =>
      new THREE.MeshBasicMaterial({
        map: texture,
        side: THREE.DoubleSide,
        transparent: false,
        opacity: 1,
      })
  )
}

/**
 * 初始化热点
 */
const initHotspot = () => {
  if (!scene || !camera || !controls) return
  // 清理旧的热点
  if (hotspotObj.value) {
    scene.remove(hotspotObj.value)
    hotspotObj.value.element.remove()
    hotspotObj.value = null
  }
  
  const worldHotspot = props.worldHotspot
  // 如果没有热点数据，使用默认视角
  if (!worldHotspot && (!props.cooradinate || Object.keys(props.cooradinate).length === 0)) {
    const defaultTarget = new THREE.Vector3(0, 0, 1)
    camera.lookAt(defaultTarget)
    controls.target.copy(defaultTarget)
    controls.update()
    return
  }

  const source = worldHotspot || props.cooradinate
  const rawWorldPos = new THREE.Vector3(source?.x || 0, source?.y || 0, source?.z || 0)
  const maxAbs = Math.max(
    Math.abs(rawWorldPos.x),
    Math.abs(rawWorldPos.y),
    Math.abs(rawWorldPos.z),
    1
  )
  const scaleRatio = CUBE_HALF_SIZE / maxAbs
  const hotspotPos = rawWorldPos.multiplyScalar(scaleRatio)
  
  // 只在 showHotspot 为 true 时渲染热点标记
  if (props.showHotspot) {
    const hotspotDom = document.createElement('div')
    hotspotDom.className = 'pano-hotspot'
    hotspotDom.title = `热点世界坐标: (${source?.x ?? 0}, ${source?.y ?? 0}, ${source?.z ?? 0})`

    const canvas = document.createElement('canvas')
    canvas.width = 96
    canvas.height = 96
    const ctx = canvas.getContext('2d')
    if (ctx) {
      ctx.clearRect(0, 0, canvas.width, canvas.height)
      ctx.lineWidth = 6
      ctx.strokeStyle = '#ff6600'
      ctx.shadowColor = 'rgba(255, 165, 0, 0.85)'
      ctx.shadowBlur = 12
      ctx.beginPath()
      ctx.arc(canvas.width / 2, canvas.height / 2, 30, 0, Math.PI * 2)
      ctx.stroke()
    }
    hotspotDom.appendChild(canvas)

    const cssObj = new CSS2DObject(hotspotDom)
    const invertedPos = hotspotPos.clone().multiplyScalar(-1)
    cssObj.position.set(invertedPos.x, invertedPos.y, invertedPos.z)
    scene.add(cssObj)
    hotspotObj.value = cssObj
  }
  
  // 无论是否显示热点标记，都对齐相机到热点位置
  const invertedPos = hotspotPos.clone().multiplyScalar(-1)
  camera.position.set(invertedPos.x, invertedPos.y, invertedPos.z)
  camera.lookAt(new THREE.Vector3(0, 0, 0))
  controls.target.set(0, 0, 0)
  controls.update()
}

/**
 * 窗口尺寸适配
 */
const handleResize = () => {
  if (!camera || !renderer || !cssRenderer.value || !panoramaContainer.value)
    return
  const container = panoramaContainer.value

  camera.aspect = container.clientWidth / container.clientHeight
  camera.updateProjectionMatrix()
  renderer.setSize(container.clientWidth, container.clientHeight)
  cssRenderer.value.setSize(container.clientWidth, container.clientHeight)
}

/**
 * 初始化Three.js场景（新增滚轮事件绑定）
 */
const initThree = async () => {
  if (!panoramaContainer.value) {
    console.error('全景图容器不存在')
    return
  }

  const container = panoramaContainer.value
  container.innerHTML = ''

  // 初始化场景、相机、渲染器（原有逻辑不变）
  scene = new THREE.Scene()
  const initialFov = props.cooradinate.fov || 75
  camera = new THREE.PerspectiveCamera(
    initialFov, // 初始FOV
    container.clientWidth / container.clientHeight,
    0.1,
    1000
  )
  camera.position.set(0, 0, 0)

  renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false })
  renderer.setSize(container.clientWidth, container.clientHeight)
  renderer.setPixelRatio(window.devicePixelRatio)
  container.appendChild(renderer.domElement)

  // 初始化立方体
  //console.log('加载图片数组:', props.images);
  const cubeMaterials = await loadCubeTextures(props.images)
  const cube = new THREE.Mesh(
    new THREE.BoxGeometry(
      CUBE_HALF_SIZE * 2,
      CUBE_HALF_SIZE * 2,
      CUBE_HALF_SIZE * 2
    ),
    cubeMaterials
  )
  cube.geometry.scale(1, 1, -1)
  scene.add(cube)

  // 全局坐标轴辅助（始终可见）
  axesHelper = new THREE.AxesHelper(CUBE_HALF_SIZE * 1.5)
  scene.add(axesHelper)

  // 初始化OrbitControls
  controls = new OrbitControls(camera, renderer.domElement)
  controls.minDistance = 0.1
  controls.maxDistance = 10
  controls.zoomSpeed = 10
  controls.enablePan = true
  controls.target.set(0, 0, 0)
  controls.enableLookAt = true
  controls.enableRotate = true
  controls.rotateSpeed = 1
  // 禁用Controls默认滚轮缩放
  controls.enableZoom = false
  controls.maxPolarAngle = Math.PI / 2.5

  controls.listenToMouseEvents = (domElement: HTMLElement) => {
    // 移除window监听，改为监听Canvas自身
    domElement.addEventListener('mousedown', controls.onMouseDown)
    domElement.addEventListener('mousemove', controls.onMouseMove) // 改为Canvas监听
    domElement.addEventListener('mouseup', controls.onMouseUp)
    domElement.addEventListener('mouseleave', controls.onMouseUp)
  }
  // 绑定到当前Canvas
  controls.listenToMouseEvents(renderer.domElement)

  controls.domElement.style.pointerEvents = 'auto'
  controls.listenToKeyEvents(window)

  // 绑定滚轮事件（监听Canvas的滚轮，调整FOV）
  const canvas = renderer.domElement
  canvas.addEventListener('wheel', handleWheelForFov)

  // 初始化CSS2D渲染器和热点
  cssRenderer.value = new CSS2DRenderer()
  cssRenderer.value.setSize(container.clientWidth, container.clientHeight)
  cssRenderer.value.domElement.style.position = 'absolute'
  cssRenderer.value.domElement.style.top = '0'
  cssRenderer.value.domElement.style.left = '0'
  cssRenderer.value.domElement.style.pointerEvents = 'none'
  container.appendChild(cssRenderer.value.domElement)

  initHotspot()

  if (!props.cooradinate || Object.keys(props.cooradinate).length === 0) {
    camera.lookAt(0, 0, 1)
    camera.position.set(0, 0, 10)
    controls.target.set(0, 0, 0)
    controls.update()
  }

  window.addEventListener('resize', handleResize)
  console.log('Three.js场景初始化完成（已启用滚轮调整FOV）')
  emit('canvas-ready')
}

/**
 * 动画循环（原有逻辑不变）
 */
const animate = () => {
  if (!scene || !camera || !renderer || !cssRenderer.value || !controls) return

  animationId.value = requestAnimationFrame(animate)
  controls.update()
  renderer.render(scene, camera)
  cssRenderer.value.render(scene, camera)
}

// 重新初始化方法（原有逻辑不变）
const reinit = async () => {
  if (animationId.value) {
    cancelAnimationFrame(animationId.value)
  }
  // 移除旧滚轮事件（避免重复绑定）
  if (renderer?.domElement) {
    renderer.domElement.removeEventListener('wheel', handleWheelForFov)
  }
  await initThree()
  animate()
}

// 生命周期管理（新增滚轮事件清理）
onMounted(() => {
  nextTick(() => {
    initThree().then(animate)
  })
})

onUnmounted(() => {
  if (animationId.value) cancelAnimationFrame(animationId.value)
  if (renderer) {
    renderer.dispose()
    // 清理滚轮事件
    renderer.domElement.removeEventListener('wheel', handleWheelForFov)
  }
  //cssRenderer.value?.dispose();
  window.removeEventListener('resize', handleResize)
  if (hotspotObj.value) {
    scene?.remove(hotspotObj.value)
    hotspotObj.value.element.remove()
  }
  if (axesHelper) {
    scene?.remove(axesHelper)
    axesHelper = null
  }
})

// 监听图片或坐标变化时重新初始化
watch(
  () => [props.images, props.cooradinate, props.worldHotspot],
  () => {
    nextTick(() => {
      reinit()
    })
  },
  { deep: true }
)

const getCanvas = () => {
  if (renderer && renderer.domElement) {
    return renderer.domElement
  }
  return null
}

const getControls = () => {
  return controls
}

const getCamera = () => {
  return camera
}

defineExpose({ panoramaContainer, getCanvas, getControls, getCamera })
</script>

<style scoped>
.Panorama {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: relative;
  box-sizing: border-box;
}

.viewIcon {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 25px;
  height: 25px;
  background-color: rgba(162, 162, 162, 0.5);
  border-radius: 50%;
  cursor: pointer;
}

.fov {
  position: absolute;
  bottom: 10px;
  left: 10px;
  width: 50px;
  height: 20px;
  font-size: 20px;
  background-color: black;
}
</style>
